use axum::{extract::Request, middleware::Next, response::IntoResponse, Extension, Json};
use axum_app_common::{
    error::BizError,
    model::{AppState, BizResponse, CurrentUser, SessionContext},
    utils::Token,
};
use axum_app_service::{cache, service::rbac_svc, user_dal::UserQuery, UserStatus};
use serde_json::Value;
use std::{error::Error, sync::Arc};
use tracing::{debug, error};

pub async fn mid_handler_authc(
    Extension(state): Extension<Arc<AppState>>,
    mut req: Request,
    next: Next,
) -> impl IntoResponse {
    // debug!("mid_handler_authc request: {:?}", req);

    let headers = req.headers();

    let token = headers.get("X-Token");
    let Some(token) = token else {
        return Json(BizResponse::<Value>::fail_with_biz_error(
            axum_app_common::error::BizError::Forbidden,
            Some("缺少X-Token头"),
        ))
        .into_response();
    };

    if token.is_empty() {
        return Json(BizResponse::<Value>::fail_with_biz_error(
            axum_app_common::error::BizError::Forbidden,
            Some("缺少X-Token头"),
        ))
        .into_response();
    }

    let token = token.to_str().unwrap();

    // 解析token
    let token = match Token::parse_env(token) {
        Err(err) => {
            error!("token解析异常:{}", err);
            return Json(BizResponse::<Value>::fail_with_biz_error(
                axum_app_common::error::BizError::TokenInvalid,
                None,
            ))
            .into_response();
        }
        Ok(token) => token,
    };

    let token_data = token.payload;
    debug!("token_data: {token_data:?}");

    let userid = token_data.userid;
    let sessionid = token_data.sessionid;

    // TODO 从缓存中查询用户信息是否存在, 如果不存在, 则从数据库中查询用户和角色信息, 并设置到缓存中
    let redis_conn_pool = &state.redis_conn_pool;
    let mut redis_conn = redis_conn_pool.get().await.unwrap();

    let key = format!("users:{userid}:{sessionid}");

    let session_user =
        cache::get_or_init::<CurrentUser>(&mut redis_conn, (key.as_str(), 30), async {
            query_user_form_db(&state, &userid).await
        })
        .await;

    let session_user = match session_user {
        Ok(session_user) => session_user,
        Err(err) => {
            error!("用户信息查询异常:{}", err);
            return Json(BizResponse::<Value>::fail_with_biz_error(
                axum_app_common::error::BizError::TokenInvalid,
                None,
            ))
            .into_response();
        }
    };

    let Some(session_user) = session_user else {
        return Json(BizResponse::<Value>::fail_with_biz_error(
            axum_app_common::error::BizError::TokenInvalid,
            Some("用户信息不存在"),
        ))
        .into_response();
    };

    // 设置会话
    req.extensions_mut().insert(SessionContext {
        current_user: Some(session_user),
    });

    let resp = next.run(req).await;
    debug!("handle mid_handler_a response: {:?}", resp);
    resp
}

/// 数据库中查询用户信息
async fn query_user_form_db(
    state: &Arc<AppState>,
    userid: &str,
) -> anyhow::Result<Option<CurrentUser>> {
    let db_user = match UserQuery::find_user_by_userid(&state.db_conn, userid).await {
        Err(err) => {
            error!("数据库查询异常:{}", err);
            anyhow::bail!(BizError::DbErr);
        }
        Ok(None) => return Ok(None),
        Ok(Some(res)) => res,
    };

    if db_user.status == format!("{:?}", UserStatus::Disabled) {
        anyhow::bail!(BizError::UserDisabled);
    };

    let rbac_svc = rbac_svc::RbacSvc::new(&state.db_conn, None);
    let roles = match rbac_svc.query_user_roles(userid).await {
        Err(_) => vec![],
        Ok(roles) => roles
            .iter()
            .map(|r| r.role_code.to_string())
            .collect::<Vec<_>>(),
    };

    let curr_user = CurrentUser {
        userid: userid.to_string(),
        username: db_user.username.to_string(),
        status: db_user.status.to_string(),
        roles,
    };

    Ok(Some(curr_user))
}
